home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / PASCAL / 0191.ZIP / PGM-AIDE.PAS < prev    next >
Pascal/Delphi Source File  |  1985-11-03  |  33KB  |  586 lines

  1. {$C-}
  2. {-----------------------------------------------------------------------------}
  3. {                                                                             }
  4. {                                                                             }
  5. {         "  S o r r y ,  D a v e,   I   C a n ' t   D o   T h a t .  "       }
  6. {                                                                             }
  7. {                                                          Arthur C. Clark    }
  8. {                                                           " 2 0 0 1 "       }
  9. {-----------------------------------------------------------------------------}
  10. { Stayres Version 3.31
  11.  
  12. {  A  Turbo  "stay-resident" program clobbers the Dos register stack.  It
  13.   jumps over the Turbo run-time initialization code that would set up the
  14.   program registers  and environment.   Secondly,  a  stay-resident  program
  15.   could not ordinarily issue file I/O since that would clobber Dos interrupt
  16.   registers.  Therefore,  the following code proposes an inline solution,
  17.   providing a Turbo entry  stack  for  "stay-resident"  programs  and allowing
  18.   those programs to issue Dos I/O and other interrupts.
  19.  
  20.   This Turbo stay-resident demo has been put together to perform both Dos I/O
  21.   and Bios interrupts. It has been tested for re-entrancy and recursiveness
  22.   on an IBM PC with PCDOS 2.1, 3.1 .
  23.  
  24.   R_U_N   I_N_S_T_R_U_C_T_I_O_N_S
  25.   Separate the include files, compile to a COM file using STAYRES.??? as the
  26.   Main file. Then execute the command file and activate with the default
  27.   Alt-F10 key. It will also free its memory and return to Dos with the
  28.   Ctrl-Home key at the last "Press a key" prompt (Illustrated in the Stayxit
  29.   file). Maximum free dynamic memory should be between 300-400 paragraphs
  30.   since this demo uses a recursive stack. If you get $FF runtine errors or
  31.   "allocation error", "cannot load Command.Com", then raised the mAximum
  32.   free value to something reasonable but less than your maximum memory -
  33.   20K. This program can ONLY run as a COM file.
  34.  
  35.   For those of you already using user interrupts 60-67, please change the
  36.   User_Int constant to an interrupt number you do not currently use .
  37.  
  38.                                                  The Hunter's Helper
  39.  
  40.                                                   Lane Ferris
  41.                                                   4268 26th St
  42.                                                   San Francisco,Ca. 94131
  43.                                                   [ 70357,2716 ]
  44. }
  45. {-----------------------------------------------------------------------------}
  46. {     This code has been tested/used on an IBM PC using PC-DOS 2.10/3.1       }
  47. {-----------------------------------------------------------------------------}
  48. { Authors: Lane H. Ferris (Stay Resident/Exit Code)       }
  49. {          Neil J. Rubenking (Directory code and ideas)   }
  50. {          Other Public Gurus on whose shoulders we stand.}
  51. {
  52.  PURPOSE:  This code will serve as a template to create other "Stay  Resident"
  53.            programs  in  Turbo  Pascal(tm).   This  code  intercepts  Int  16,
  54.            displacing original Interrupt  16  Vector  to  User  Interrupt  60.
  55.            During  execution  of  other  programs,  it  can  be invoked by the
  56.            special key combination  specified  by  "Our_Char"  (in  this  case
  57.            Alt-F10.)
  58.  
  59.  Modifications:
  60.           7. 7.85 - Replace Windows with a more simple form/less code.
  61.           7. 8.85 - Replace Window Array with Pointers/Heap form.
  62.           7.11.85 - Re-issue termination Keyboard Read / pass back to user
  63.                     Would like to back up Instruction Ptr by two bytes before
  64.                     the Int 16 ($CD16) but it might be a "long call" by
  65.                     some other Kbd interceptor (chirp chirp chrip)... and
  66.                     thats "trouble in River City".
  67.           7.19.85 - Clean up RmWin "incorrect" attribute bugs. If screen
  68.                     isnt cleared, we get border attribute, not text attrb.
  69.                   - Remove last window at Termination Time (Ctrl-Home).
  70.           8.26.85 - Version 3.10 Changes
  71.                     1) Save 40 words in StaySave/Rstr to avoid clobbering
  72.                        Dos Stack when entering Dos with Turbo Write(ln) caused
  73.                        by Int 21 Function 5  (Writln(Lst,..)) which re-issues
  74.                        Int 16.
  75.                     2) Change Int 68 to Int 67 to Avoid collisions with
  76.                        Dos 3.1 on an AT.
  77.                     3) Correct "Press a Key..." to accept any "Key..."
  78.                        (not just Cr).
  79.                     4) Check Int16 function. Jmp directly to Int16 if not
  80.                        a character request. Avoids 40 word Save/Restore
  81.                        overhead.
  82.           9.04.85 - Version 3.20 changes
  83.                     When returning to user program, pass back a fake
  84.                     "Ctrl-key" scan code to allow immediate re-execution
  85.                     of the TSR (Terminate Stay Resident) program. Also
  86.                     solves SideKick incessant bird caws.
  87.           9.18.85 - Version 3.2C
  88.                     When saving/restoring the stack, save 40 words or less
  89.                     depending on stack size (0-Sp = stack size) to avoid
  90.                     overflowing into SS:0 when stack is less than 40 words.
  91.                     Put back the "wait for user key logic" at Demo end.
  92.                     Beep like SuperKick if Key is OurKey
  93.                     Version 3.31
  94.           9.19.85   Futz around with the "wait for user key logic", allow
  95.                     the Our_key to pass, but beep user to show we aint
  96.                     gonna activate, cuz our InUse bit is still set.
  97.                     Clean up the documentation and duplicate instructions in
  98.                     StaySave/Rstr.
  99.                     Change Int67 to Int60 for Fun and Profit and get around
  100.                     Mark Stanock's use of those locations.
  101. }
  102. Program Stay_Resident;
  103.  
  104. { * * * * * * * CONSTANTS * * * * * * * * * * * * * * * * * * * * * * }
  105.   const
  106.     Our_Char        =  104; {this is the scan code for AltF1}
  107.     Ctrl_Home       = #119; {Control Home Scan Code          }
  108.     Quit_Key        = #119;
  109.     Ctrl_End        = #117; {Control End Scan Code           }
  110.     User_Int        = $60; {place to put new interrupt}
  111.     Kybrd_Int       = $16; {BIOS keyboard interrupt}
  112.     MaxWin          = 3;   { Max number of windows open at one time }
  113.  
  114. {  - - - - - - T Y P E    D E C L A R A T I O N S - - - - - - - - - - - -  }
  115.   Type
  116.     Regtype     = record Ax,Bx,Cx,Dx,Bp,Si,Di,Ds,Es,Flags:integer  end;
  117.     HalfRegtype = record Al,Ah,Bl,Bh,Cl,Ch,Dl,Dh:byte              end;
  118.     filename_type = string[64];
  119.  
  120. { - - - - - - - T Y P E D   C O N S T A N T S - - - - - - - - - - - - - - -}
  121.   Const
  122.     {regs is defined as a typed constant in order to get it in the code segment}
  123.  
  124.       Regs   : regtype = (Ax:0;Bx:0;Cx:0;Dx:0;Bp:0;Si:0;Di:0;Ds:0;Es:0;Flags:0);
  125.  
  126.       OurDseg: integer = 0;            {Our Data Segment Value             }
  127.       OurSseg: integer = 0;            {Our Stack Segment Value            }
  128.       DosSseg: integer = 0;            {Dos Stack Segment Value            }
  129.       Inuse  : Boolean = false;        {Recursion flag                     }
  130.      { The following two constants *MUST* remain in the Ip:CS order        }
  131.      { because StaySave uses them as a JMP target                          }
  132.       User_IntIP : integer = 0;        {Pointer to Original IP Int value   }
  133.       User_IntCs : integer = 0;        {Pointer to Original Cs Int value   }
  134.       StackSize  : integer = 0;        {Current User/or Dos Stack word size}
  135.  
  136.  { - - - - - - - V A R I A B L E S - - - - - - - - - - - - - - - - - - - - - -}
  137.     Var
  138.       SaveRegs                      : regtype;
  139.       HalfRegs                      : halfregtype absolute regs;
  140.       Terminate_flag                : boolean ;
  141.       Keychr                        : char ;
  142.       Old_Xpos,Old_Ypos             : integer ;
  143.  
  144.  
  145. {-----------------------------------------------------------------------------}
  146. {                 W  I  N  D  O  W     R  O  U  T  I  N  E                    }
  147. {---------------------------------------------------------------------------- }
  148.  
  149. {$I WINDO.INC}
  150.  
  151. {-----------------------------------------------------------------------------}
  152. {            S  T  A  Y  E  X  I  T                                           }
  153. {-----------------------------------------------------------------------------}
  154. {$I STAYXIT.320}
  155.  
  156. {----------------------------------------------------------------------}
  157. {          B e e p   :  S o u n d  t h e  H o r n                      }
  158. {----------------------------------------------------------------------}
  159. Procedure Beep(N :integer); {------------------------------------------}
  160.    Begin                    {  This routine sounds a tone of frequency }
  161.       Sound(n);             {  N for approximately 100 ms              }
  162.       Delay(100);           {------------------------------------------}
  163.       Sound(n div 2);
  164.       Delay(100);
  165.       Nosound;
  166.       End {Beep} ;
  167.  
  168. {-----------------------------------------------------------------------------}
  169. {            THE FOLLOWING ARE THE USER INCLUDE ROUTINES                      }
  170. {-----------------------------------------------------------------------------}
  171.  
  172. {-----------------------------------------------------------------------------}
  173. {            E  P  S  O  N    P  R  I  N  T  E  R    S  E  T  U  P            }
  174. {-----------------------------------------------------------------------------}
  175. Procedure EP;
  176. {$I EP.INC}
  177.  
  178. {-----------------------------------------------------------------------------}
  179. {            S  C  R  E  E  N    C  O  U  N  T  E  R                          }
  180. {-----------------------------------------------------------------------------}
  181. Procedure SC;
  182. {$I SC.INC}
  183.  
  184. {-----------------------------------------------------------------------------}
  185. {            P  R  O  G  R  A  M    A  I  D  E                                }
  186. {-----------------------------------------------------------------------------}
  187. Procedure PgmAide;
  188. {$I PGM-AIDE.DVR}
  189.  
  190. {-----------------------------------------------------------------------------}
  191. {            THE ABOVE ARE THE USER INCLUDE ROUTINES                          }
  192. {-----------------------------------------------------------------------------}{---------------------------------------------
  193. {-----------------------------------------------------------------------------}
  194. {              P R O C E S S   I N T E R R U P T                              }
  195. { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
  196. Procedure Process_Intr;
  197.  
  198. {  PURPOSE:  This  procedure  replaces  the  standard  keyboard  interrupt.  If
  199.             anything but <Alt>-F1 is pressed,  the key is  passed  on  to  the
  200.             standard  keyboard  interrupt.  B*U*T  when  <Alt>- F1 is pressed,
  201.             this program takes over.  The variable InUse  is  set  to  TRUE  to
  202.             ensure that this code doesn't try to run "on top of itself " AND to
  203.             indicate  to the Inline code to save/restore the original interrupt
  204.             regs.
  205. }
  206. Begin
  207.           { K e y b o a r d    Interrupt   o c c u r s   here }
  208. {----------------------------------------------------------------------}
  209. { This could also have the include data as $I StaySave.331 }
  210. {****************************************************************************}
  211. {                 S  T  A  Y  S  A  V  E  .  I  N  C                         }
  212. {                                                                            }
  213. {           This is the Staysave.Inc file included above                     }
  214. {                                                                            }
  215. {      Separate the code out into a file or replace the $I Staysave.331      }
  216. {                statement above with this code.                             }
  217. {****************************************************************************}
  218.  
  219. {This Inline routine will save the regs and Stack for Stay resident programs.
  220.  It restores DS and SS from the previously saved integer constants "OurDseg"
  221.  and "OurSSeg". This is important since Dos is not re-entrant and any attempt
  222.  to use Interrupt I/O services will clobber the very stack on which the
  223.  Resident Turbo program just saved its regs. Thus, on the final return, you
  224.  and Toto will end up somewhere other than Kansas and without your Ruby Reds.
  225.    }
  226.  
  227. { Arthor:      Lane Ferris
  228.               - The Hunter's Helper -
  229.  
  230.        Distributed to the Public Domain for use without profit.
  231.                     Original Version 5.15.85
  232. }
  233.            { On entry the Stack will already contain: }
  234.            {  1) Sp for Dos                           }
  235.            {  2) Bp for Dos                           }
  236.            {  3) Ip for Dos                           }
  237.            {  4) Cs for Dos                           }
  238.            {  5) Flags for Dos                        }
  239.   Inline (
  240.  
  241.  { The following routine avoids the overhead of saving the DOS stack         }
  242.  { when the INT 16 function was not for a character request. This happens    }
  243.  { often (every four chars) as DOS checks on ^S/^Q/^C/Keypressed  ad.nausea  }
  244.  
  245.    $80/$FC/$00/                        {Cmp Ah,00     If Char request,       }
  246.    $74/$07/                            {Je   ChrRqst  enter Staysave code    }
  247.    $5D/$5D/                            {Pop  Bp/Pop Bp else  Restore Bp &    }
  248.    $2E/                                {     Jump to Original Dos Interrupt  }
  249.    $FF/$2E/User_IntIP/                 {Jmp Far Cs:[User_IntIp]              }
  250.  
  251.             { Move the current active registers to a save place}
  252.  
  253. {ChrRqst: }
  254.     $FA /                              {Cli         Stop all interrupts       }
  255.                                        { Bp and Sp aready saved at Begin Stmt }
  256.     $55/                               {Push   Bp  Save again for Regpak      }
  257.     $BD/Regs/                          {Mov    Bp,offset REGS address savearea}
  258.     $2E/$89/$46/$00/                   {CS:Mov [Bp+0],AX Save Users Registers }
  259.     $2E/$89/$5E/$02/                   {Cs:Mov [Bp+2],Bx}
  260.     $2E/$89/$4E/$04/                   {CS:Mov [Bp+4],CX}
  261.     $2E/$89/$56/$06/                   {CS:Mov [Bp+6],DX}
  262.     $2E/$8F/$46/$08/                   {Pop    Cs:[Bp+8] Fetch Bp from stack  }
  263.     $2E/$89/$76/$0A/                   {CS:Mov [Bp+A],SI}
  264.     $2E/$89/$7E/$0C/                   {CS:Mov [Bp+C],DI}
  265.     $2E/$8C/$5E/$0E/                   {CS:Mov [Bp+E],DS}
  266.     $2E/$8C/$46/$10/                   {CS:Mov [Bp+10],ES}
  267.     $9C/                               {PUSHF  put Flags on stack to retrieve }
  268.     $2E/$8F/$46/$12/                   {POP Cs:[Bp+12]}
  269.  
  270.                { If Current SS := [OurSseg] or (Inuse = True), }
  271.                { then dont overlay the previously saved stack. }
  272.                { This program is being recursive.              }
  273.  
  274.      $2E/$80/$3E/Inuse/$01/   {Cmp  Cs:[Inuse],1   Inuse = True ?         }
  275.      $74/$62/                 {Je   ReCurin        Yes, -J-U-M-P-         }
  276.  
  277.                { Switch the SS:Sp reg pair over to ES:Si       }
  278.                { Put Turbo's Stack pointers into SS:Sp         }
  279.  
  280.      $2E/$8C/$16/DosSSeg/     {Mov  Cs:DosSSeg,SS Save Dos Stack Segment    }
  281.      $8C/$D6/                 {Mov  Si,SS         Es gets Dos stack         }
  282.      $8E/$C6/                 {Mov  Es,Si                                   }
  283.      $2E/$8E/$16/OurSSeg/     {Mov  SS,Cs:OurSSeg SS Gets our Stack segment }
  284.      $2E/$8E/$1E/OurDseg/     {Mov  Ds,Cs:Our_Ds  DS Gets our Data Segment  }
  285.  
  286.                { If ES:Si (stack ptr) <>  OurSSeg  then        }
  287.                { Sp := Virgin Turbo Stack pointer.             }
  288.                { If Es:Si := OurSSeg, then this is a Read or   }
  289.                { Write before Inuse was set True. Dont clobber }
  290.                { the current setting of Turbo stack pointer.   }
  291.  
  292.      $2E/$3B/$36/OurSSeg/     {Cmp  Si,Cs:OurSSeg If SS := OurSSeg then     }
  293.      $89/$E6/                 {Mov  Si,Sp         dont clobber saved regs   }
  294.      $74/$05/                 {Je   $+5           else get virgin stack ptr }
  295.      $3E/$8B/$36/$74/$01/     {Mov  Si,Ds:[174]   ..(cf. code at B2B 3.0x)  }
  296.      $87/$F4/                 {Xchg Sp,Si         Set new  Stack Pointer    }
  297.  
  298.                { Stack Dos/User interrupted pgm regs for Exit. }
  299.                { These are the original interrupt process regs }
  300.                { that must be returned on interrupt return     }
  301.  
  302.      $2E/$FF/$76/$00/         {Push [Bp+0]  Save Ax                         }
  303.      $2E/$FF/$76/$02/         {Push [Bp+2]  Save Bx                         }
  304.      $2E/$FF/$76/$04/         {Push [Bp+4]  Save Cx                         }
  305.      $2E/$FF/$76/$06/         {Push [Bp+6]  Save Dx                         }
  306.                               {Push [Bp+8]  Save Bp                         }
  307.      $2E/$FF/$76/$0A/         {Push [Bp+A]  Save Si                         }
  308.      $2E/$FF/$76/$0C/         {Push [Bp+C]  Save Di                         }
  309.      $2E/$FF/$76/$0E/         {Push [Bp+E]  Save Ds                         }
  310.      $2E/$FF/$76/$10/         {Push [Bp+10] Save Es                         }
  311.  
  312.                { Now stack the lesser of current stack size or  }
  313.                { 40 Words to our stack, to be re-stack on the   }
  314.                { interrupted pgms stack on exit. This is done   }
  315.                { to allow recursive entry into Dos/or other non }
  316.                { re-entrant pgms.                               }
  317.  
  318.      $29/$C9/                 {Sub  Cx,Cx  Find minimum of current stack    }
  319.      $29/$F1/                 {Sub  Cx,Si  size or 40 words to save.        }
  320.      $D1/$E9/                 {Shr  Cx,1   Stackbytes/2 for words.          }
  321.      $83/$F9/$40/             {Cmp  Cx,+40 This keeps up from overrunning   }
  322.      $7E/$03/                 {Jle  $+3    the Stack Segment when it is less}
  323.      $B9/$40/$00/             {Mov  Cx,40  than Dos stack size              }
  324.      $2E/$89/$0E/StackSize/   {Mov  Cs:StackSize,Cx Save current stack size }
  325.  {Restack:}
  326.      $26/$FF/$34/             {Push Es:[Si] Our Stack := Dos Es:Si          }
  327.      $46/$46/                 {Inc  Si/Inc Si Get Next Dos Stack Word       }
  328.      $E2/$F9/                 {Loop to Restack                              }
  329.  
  330.      $56/                     {Push Si            Save bottom of Dos Stack  }
  331.      $2E/$8C/$5E/$0E/         {Mov  Cs:[Bp+E],Ds  Set New Data Segmt in regs}
  332. {Recurin}                     {                     Jump here if Recursion  }
  333.      $FB                      {Sti Enable Interrupts                        }
  334.  
  335.        ) ;
  336. {...........................................................................}
  337. {---------------------------------------------------------------------------}
  338. {           END OF THE STAYSAVE REGISTER ROUTINE                            }
  339. {---------------------------------------------------------------------------}
  340.  
  341. {  Set New Data Segmt in regs}
  342. {----------------------------------------------------------------------}
  343. { Check the Int 16 request function in Ah reg:
  344.                    0 = read character from Keyboard
  345.                    1 = check character available
  346.                    2 = check shift key values
  347. }
  348.  
  349.           { HalfRegs.Ah = 0  This is a Character Request because StaySave }
  350.           { doesnt allow an enter here unless it is!                      }
  351.  
  352.   Intr (User_Int, Regs);             { Use the DOS replaced interrupt}
  353.                                      { Get Key from Keyboard         }
  354.   If (Halfregs.Ah = Our_Char)        { Separate the tests so code    }
  355.                                      { performs efficiently.         }
  356.      then if  (not InUse) then       { Must be OUR key and not busy  }
  357.  
  358.         Begin { PROGRAM AIDE }
  359.         InUse := true;                  { "dont clobber saved stack"}
  360. {--------------------------------------------------------------------------}
  361. {                       INVOKE USER PROCEDURE HERE                         }
  362. {--------------------------------------------------------------------------}
  363.         PgmAide;
  364. {--------------------------------------------------------------------------}
  365. {                       END USER PROCEDURE HERE                            }
  366. {--------------------------------------------------------------------------}
  367.         While (not Keypressed);          { Get input key for the users     }
  368.          SaveRegs.Ax := 0 ;               {           interrupted program   }
  369.          Intr(User_Int,SaveRegs);         { Get the Scancode                }
  370.          Regs.Ax := SaveRegs.Ax;
  371.          If HalfRegs.Ah = Our_Char then Beep(650);
  372.  
  373.         InUse := false;                  { ok to restore interrupted stack }
  374.         End;  { PROGRAM AIDE }
  375. {---------------------------------------------------------------------------}
  376. {---------------------------------------------------------------------------}
  377. {             BEGINNING OF THE STAYRSTR ROUTINE                             }
  378. {---------------------------------------------------------------------------}
  379. { This could also be used as $I StayRstr.331 }
  380. {****************************************************************************}
  381. {                  S  T  A  Y  R  S  T  R  .  I  N  C                        }
  382. {                                                                            }
  383. {           This is the StayRstr.Inc file included above                     }
  384. {       Separate the code out into a file or replace the $I StayRstr.331     }
  385. {                  statement above with this code.                           }
  386. {****************************************************************************}
  387. {Version 3.31}
  388.                { Inline Code to restore the stack and regs moved}
  389.                { to the Turbo Resident Stack which allows       }
  390.                { re-entrancy into Dos for I/O and  recursion    }
  391.                { for Turbo Terminate & Stay Resident programs.  }
  392.  
  393.        { Author: Lane Ferris                                       }
  394.        {         - The Hunter's Helper -                           }
  395.        { Distributed to the Public Domain for use without profit.  }
  396.        { Original Version 5.15.85                                  }
  397. {----------------------------------------------------------------------}
  398. {        Restore the Dos (or interrupted pgm) Regs and Stack           }
  399. {----------------------------------------------------------------------}
  400.            { On entry the Stack will already contain: }
  401.            {    Pointer to bottom of stack            }
  402.            {    Bottom of Dos Stack Ptr               }
  403.            {    StackSize words of saved pgm stack    }
  404.            {    Dos Flags                             }
  405.            {    Dos Code Segment                      }
  406.            {    Dos Instruction Ptr                   }
  407.            {    Dos Base Pointer                      }
  408.            {    Dos Original Stack Ptr                }
  409.  
  410.  
  411.            { Retrieve the Regpack registers as they were    }
  412.            { stored for the Interrupt Entry.                }
  413.  
  414.     inline(
  415.     $BD/Regs/                          {Mov    Bp,offset REGS}
  416.     $2E/$8B/$46/$00/                   {CS:Mov Ax,[Bp+0]}
  417.     $2E/$8B/$5E/$02/                   {Cs:Mov Bx,[Bp+2]}
  418.     $2E/$8B/$4E/$04/                   {CS:Mov Cx,[Bp+4]}
  419.     $2E/$8B/$56/$06/                   {CS:Mov Dx,[Bp+6]}
  420.  
  421.     $2E/$8B/$76/$0A/                   {CS:Mov Si,[Bp+A]}
  422.     $2E/$8B/$7E/$0C/                   {CS:Mov Di,[Bp+C]}
  423.     $2E/$8E/$5E/$0E/                   {CS:Mov DS,[Bp+E]}
  424.     $2E/$8E/$46/$10/                   {CS:Mov ES,[Bp+10]}
  425.     $2E/$FF/$76/$12/                   {Push Cs:[Bp+12]  }
  426.                                        {PopF             }
  427.             { The following code was added to avoid }
  428.             { the 80286 Pop flag (POPF) bug which   }
  429.             { enables interrupts while we are trying}
  430.             { to POP  the stack on odd byte boundry }
  431.     $EB/$01/                           {JMP $+3 Skip over IRET   }
  432.     $CF/                               {IRET    POP IP/CS/Flags  }
  433.     $0E/                               {PUSH CS Make a return    }
  434.     $E8/$FB/$FF/                       {CALL CS:$-2 Pop the Flags}
  435.  
  436.            { If [Cs:InUse]:= True,  then dont restore the stack.}
  437.            { This program is being recursive. Else restore  Dos }
  438.            { Stack and Program Entry registers for final exit.  }
  439.  
  440.      $2E/$80/$3E/Inuse/$01/   {Cmp  byte ptr Cs:[Inuse],1                   }
  441.      $74/$25/                 {Je   ReCurOut   J-U-M-P                      }
  442.  
  443.            { Move "StackSize" words back to the interrupted pgms}
  444.            { stack. The originals could have been clobber by our}
  445.            { being recursive. (Especially true of DOS)          }
  446.  
  447.     $FA /                    { Cli      ; Stop all interrupts    }
  448.     $5E/                     {Pop Si     Bottom of Dos Stack              }
  449.     $2E/$8B/$0E/StackSize/   {Mov Cx,Cs:StackSize Saved Stack Words       }
  450.     $2E/$8E/$06/DosSSeg/     {Mov ES,Cs:DosSSeg Get Dos StackSegment      }
  451.   {Restack:}
  452.     $4E/$4E/                 {Dec Si/Dec Si     Backup Dos Stack          }
  453.     $26/$8F/$04/             {Pop Es:[Si]       Dos Stack := Our Stack    }
  454.     $E2/$F9/                 {Loop to Restack                             }
  455.     $89/$F5/                 {Mov Bp,Si         Save Dos Sp across Pops   }
  456.  
  457.            {         - C - A - U - T - I - O - N -              }
  458.            { Restore the original interrupted programs regs     }
  459.            { except Ax. Ax usually contains status. It contains }
  460.            { a scan code and key for Int 16. You may want to    }
  461.            { rework this if using another interrupt.            }
  462.  
  463.     $07/                     {Pop  Es                                     }
  464.     $1F/                     {Pop  Ds                                     }
  465.     $5F/                     {Pop  Di                                     }
  466.     $5E/                     {Pop  Si                                     }
  467.     $5A/                     {Pop  Dx                                     }
  468.     $59/                     {Pop  Cx                                     }
  469.     $5B/                     {Pop  Bx                                     }
  470.     $44/$44/                 {Inc sp/Inc sp Thow old Ax value away        }
  471.  
  472.     $89/$EC/                  {Mov  Sp,Bp         Setup Dos Stack Ptr     }
  473.     $2E/$8E/$16/DosSSeg/      {Mov  SS,Cs:DosSSeg Give back Dos Stack     }
  474.  
  475. {RecurOut}                             {Clean up the Stack                }
  476.     $5D/                               {Pop Bp     Throw away old dos Sp  }
  477.     $BD/Regs/                          {Mov Bp,offset REGS                }
  478.     $2E/$FF/$76/$12/                   {Push Cs:[Bp+12]  Flags from last  }
  479.                                        {PopF             interrupt.       }
  480.             { The following code was added to avoid  }
  481.             { the 80286 Pop flag (POPF) bug which    }
  482.             { enables interrupts while we are trying }
  483.             { to POP  the stack on odd byte boundry  }
  484.  
  485.     $EB/$01/                           {JMP $+3 Skip over IRET   }
  486.     $CF/                               {IRET    POP IP/CS/Flags  }
  487.     $0E/                               {PUSH CS Make a return    }
  488.     $E8/$FB/$FF/                       {CALL CS:$-2 Pop the Flags}
  489.  
  490.     $5D/                               {Pop Bp  Retrieve old BP        }
  491.     $FB/                               {Sti     Enable interrupts      }
  492.     $CA/$02/$00                        {Ret Far 002 Thow old flags away}
  493.         );
  494. {.......................................................................}
  495. {---------------------------------------------------------------------------}
  496. {            END OF THE STAYRSTR ROUTINE                                    }
  497. {---------------------------------------------------------------------------}
  498.  
  499. End ;{Process_Intr}
  500. {-----------------------------------------------------------------------}
  501.  
  502. {-------------------------------------------------------------------------}
  503. {                             M  A  I  N                                  }
  504. {-------------------------------------------------------------------------}
  505.           { The main program installs the new interrupt routine }
  506.           { and makes it permanently resident as the keyboard   }
  507.           { interrupt.  The old keyboard interrupt is addressed }
  508.           { through #60H, so it can still be used.              }
  509.           {                                                     }
  510.           { The following dos calls are used:                   }
  511.           { Function 25 - Install interrupt address             }
  512.           {               input al = int number,                }
  513.           {               ds:dx = address to install            }
  514.           { Function 35 - get interrupt address                 }
  515.           {               input al = int number                 }
  516.           {               output es:bx = address in interrupt   }
  517.           { Function 31 - terminate and stay resident           }
  518.           {               input dx = size of resident program   }
  519.           {               obtained from the memory              }
  520.           {               allocation block at [Cs:0 - $10 + 3]  }
  521.           { Function 49 - Free Allocated Memory                 }
  522.           {               input Es = Block Segment to free      }
  523.           { Interrupt 20 - Return to invoking process           }
  524.           {-----------------------------------------------------}
  525.  
  526. {-----------M A I N    B L O C K---------------------------------------------}
  527. Begin                                  {**main**}
  528.  
  529.   InUse  := false;
  530.   OurDseg:= Dseg;           { Save the Data Segment Address for Interrupts }
  531.   OurSseg:= Sseg;           { Save our Stack Segment for Interrupts        }
  532.  
  533.  
  534.   Terminate_Flag := false ;
  535.   SaveRegs.Es := 00;     { clear for Dos 3.0 bug }
  536.   {now install the interrupt routine}
  537.  
  538.   SaveRegs.Ax := $3500 + User_Int;
  539.   Intr($21,SaveRegs);                 {Check to make sure int not already used}
  540.  
  541.   if SaveRegs.Es <> $00 then
  542.     WriteLn ('Interrupt in use -- can''t install Resident Turbo Code')
  543.   else
  544.     begin
  545.  
  546.  
  547.       SaveRegs.Ax := $3500 + Kybrd_Int;
  548.       Intr($21,SaveRegs);        {get the address of keyboard interrupt }
  549.  
  550.       SaveRegs.Ax := $2500 + User_Int;
  551.       SaveRegs.Ds := SaveRegs.Es;
  552.       SaveRegs.Dx := SaveRegs.Bx;
  553.       Intr($21,SaveRegs);       { set the user-interrupt address to point
  554.                                 { to the keyboard interrupt address }
  555.  
  556.       SaveRegs.Ax := $2500 + Kybrd_Int;
  557.       SaveRegs.Ds := Cseg;
  558.       SaveRegs.Dx := Ofs(Process_Intr);
  559.       Intr ($21,SaveRegs);        { set the keyboard interrupt to point to
  560.                                   "Process-Intr" above}
  561.  
  562.       User_IntIp := MemW[0:User_Int * 4 ];  { Location of User Interrupt Ip }
  563.       User_IntCs := MemW[0:User_Int * 4 +2];{ Location of User Interrupt Cs }
  564.  
  565. {----------------------------------------------------------------------------}
  566. {                INITIALIZE YOUR PROGRAM HERE                                }
  567. {----------------------------------------------------------------------------}
  568.       { Initialize Your Progam Here since you wont get control again
  569.         until "Our_Char" is entered from the Keyboard.               }
  570.       Writeln('  Turbo Stay-Resident Program Aide');
  571.       Writeln('        Press Alt-F1 to Call');
  572.  
  573. {----------------------------------------------------------------------------}
  574. {               END OF INITALIZE PROGRAM CODE                                }
  575. {----------------------------------------------------------------------------}
  576.  
  577.       {now terminate and stay resident}
  578.                                               { Pass return code of zero    }
  579.       SaveRegs.Ax := $3100 + 0 ;              { Terminate and Stay Resident }
  580.       SaveRegs.Dx := MemW [Cseg-1:0003] ;     { Prog_Size from Allocation Blk}
  581.       Intr ($21,SaveRegs);
  582.  
  583.     end;
  584.        { END OF RESIDENCY CODE }
  585. end.
  586.